﻿using HK.Core.Common;
using HK.Core.Common.Data;
using HK.Core.Counter;
using HK.Core.Counter.Protocol;
using HK.Core.Extend;
using HK.Core.Load.Protocol.Data;
using HK.Core.Load.Signal;
using HK.Core.Utils;

namespace HK.Core.Load.Data
{
    
    /// <summary>
    /// 加载资产基类
    /// </summary>
    public abstract class AssetBase : JsonData<AssetBase>, IAssetBase
    {
        
        /// <summary>
        /// 缓存池Id
        /// </summary>
        public long PoolId { get; set; } = -1L;
        
        /// <summary>
        /// AssetKey
        /// </summary>
        public string Key { get; protected set; } = null;

        /// <summary>
        /// 加载资产类型
        /// </summary>
        public virtual LoadAssetType LoadType => LoadAssetType.UnKnown;

        /// <summary>
        /// 异步标志位
        /// <para>* true : 异步加载</para>
        /// <para>* false : 同步加载</para>
        /// </summary>
        public bool Async { get; private set; } = false;

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(Key);
        
        /// <summary>
        /// 设定信息
        /// </summary>
        /// <param name="iKey">AssetKey</param>
        /// <param name="iOptions">选项</param>
        /// <param name="iAsync">异步标志位</param>
        /// <param name="iCoolTime">倒计时冷却时间(默认 : 30秒)</param>
        public virtual void SetInfo(string iKey, int iOptions, bool iAsync = false, float iCoolTime = Const.DefaultCoolTime)
        {
            Key = iKey;
            Options = iOptions;
            Async = iAsync;
            
            // 延迟释放
            if (Delay)
            {
                CountDown = TimeCounter.Create(OnCounterUpdated, iCoolTime);
            }
        }
        
#region Options

        /// <summary>
        /// 加载选项
        /// </summary>
        public int Options { get; private set; } = (int)LoadOptions.Auto;
        
        /// <summary>
        /// 延迟释放
        /// <para>* 引用计数小于等于0时，才开始倒计时释放</para>
        /// <para>* 既:倒计时释放</para>
        /// </summary>
        public bool Delay
        {
            get => UtilsBytes.CheckBytes(Options, (int)LoadOptions.Delay);
            private set => Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Auto, value);
        }

        /// <summary>
        /// 自动释放
        /// <para>* 既:引用计数释放</para>
        /// </summary>
        public bool Auto
        {
            get => UtilsBytes.CheckBytes(Options, (int)LoadOptions.Auto);
            private set => Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Auto, value);
        }

        /// <summary>
        /// 立即释放
        /// </summary>
        public bool ReleaseImmediately
        {
            get => UtilsBytes.CheckBytes(Options, (int)LoadOptions.ReleaseImmediately);
            private set
            {
                Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.ReleaseImmediately, value);
                if (value)
                {
                    Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Delay, false);
                    Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Auto, false);
                }
            }
        }

        /// <summary>
        /// 永生
        /// </summary>
        public bool Immortal
        {
            get => UtilsBytes.CheckBytes(Options, (int)LoadOptions.Immortal);
            private set
            {
                Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Immortal, value);
                if (value)
                {
                    Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Delay, false);
                    Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.Auto, false);
                    Options = UtilsBytes.SetBytes(Options, (int)LoadOptions.ReleaseImmediately, false);
                }
            }
        }

        /// <summary>
        /// 升级加载选项
        /// </summary>
        /// <param name="iOptions">加载选项</param>
        public void UpgradeLoadOptions(int iOptions)
        {
            if(iOptions <= Options) return;

            var lastOptions = Options;
            // 永生
            if (UtilsBytes.CheckBytes(iOptions, (int)LoadOptions.Immortal))
            {
                // 若已是永生
                if(Immortal) return;
                
                Immortal = true;
                ReferenceCount = 0;
                StopTimeCounter();
                return;
            }
            
            // 立即释放
            if (UtilsBytes.CheckBytes(iOptions, (int)LoadOptions.ReleaseImmediately))
            {
                // 若当前是永生，则什么都不做
                if(Immortal) return;
                // 若已是立即释放
                if(ReleaseImmediately) return;

                ReleaseImmediately = true;
                ReferenceCount = 0;
                StopTimeCounter();
                Release();
                return;
            }
            
            // 若前一次为延迟释放，则引用计数+1，计数重置
            if (UtilsBytes.CheckBytes(iOptions, (int)LoadOptions.Auto))
            {
                // 若当前是永生，则什么都不做
                if(Immortal) return;
                // 若已是立即释放，则什么都不做
                if(ReleaseImmediately) return;
                // 若已是按引用计数释放，则引用计数+1
                if (Auto)
                {
                    ++ReferenceCount;
                    return;
                }
                
                // 若现在是延迟释放
                if (Delay)
                {
                    ++ReferenceCount;
                    // 重启倒计时
                    RestartTimeCounter();
                    return;
                }

                Auto = true;
                ++ReferenceCount;
                return;
            }

            // 延迟释放
            if (UtilsBytes.CheckBytes(iOptions, (int)LoadOptions.Delay))
            {
                // 若当前是永生，则什么都不做
                if(Immortal) return;
                // 若已是立即释放，则什么都不做
                if(ReleaseImmediately) return;
                // 若已是按引用计数释放，则引用计数+1
                if (Auto)
                {
                    ++ReferenceCount;
                    return;
                }
                
                // 重启倒计时
                RestartTimeCounter();
            }
        }

#endregion
        
#region LoadFrom

        /// <summary>
        /// 加载源头
        /// </summary>
        public LoadFrom From { get; protected set; } = LoadFrom.Unknown;

        /// <summary>
        /// 从Resources中加载的资源
        /// </summary>
        public bool FromResource => LoadFrom.Resource == From;
        
        /// <summary>
        /// 从Ab加载的资源
        /// </summary>
        public bool FromAb => LoadFrom.AssetBundle == From;
                
        /// <summary>
        /// 从Resources中的图集加载
        /// </summary>
        public bool FromResourceAtlas => LoadFrom.ResourceAtlas == From;
                
        /// <summary>
        /// 从Ab中的图集加载
        /// </summary>
        public bool FromAbAtlas => LoadFrom.AssetBundleAtlas == From;

        /// <summary>
        /// 从图集加载
        /// </summary>
        public bool FromAtlas => FromResourceAtlas || FromAbAtlas;

#endregion

#region Caller

        /// <summary>
        /// 调用者
        /// </summary>
        public string Caller { get; private set; } = null;

        /// <summary>
        /// 更新调用者
        /// </summary>
        /// <param name="iCaller"></param>
        public void UpdateCaller(string iCaller)
        {
            Caller = iCaller;
        }
        
#endregion
        
#region Use&UnUse

        /// <summary>
        /// 引用计数
        /// <para>* 当引用计数小于等于0时, 会触发自动释放。</para>
        /// </summary>
        public int ReferenceCount { get; private set; } = 0;

        /// <summary>
        /// 使用
        /// <para>* 每使用一次，该引用计数会自增一</para>
        /// </summary>
        public virtual void Use()
        {
            // 永生
            if(Immortal) return;
            
            // 延迟释放
            if (Delay && IsCounting)
            {
                // 重新计时
                RestartTimeCounter();
                
                // 发送Use信号
                // SendSignalNotify(SignalType.Use);
                return;
            }
            
            // 自动释放
            if (Auto)
            {
                ++ReferenceCount;
                
                // 发送Use信号
                // SendSignalNotify(SignalType.Use);
                return;
            }
            
            // 立即释放
            if (ReleaseImmediately)
            {
                Release();
            }
        }

        /// <summary>
        /// 撤销使用
        /// <para>* 每撤销一次，该引用计数会自减一, 当引用计数小于等于0时，会自动释放或者触发倒计时释放</para>
        /// </summary>
        /// <param name="iForce">强制标志位(会强制将引用计数清零，后强制释放)</param>
        public virtual void UnUse(bool iForce = false)
        {
            // 永生
            if(Immortal) return;
            
            // 非强制释放
            if (iForce || ReleaseImmediately)
            {
                ReferenceCount = 0;
                if (Delay)
                {
                    StopTimeCounter();
                }
                Release();
                
                // 发送Use信号
                SendSignalNotify(SignalType.UnUse);
                return;
            }
            
            // 自动释放
            if (Auto)
            {
                --ReferenceCount;
                if (0 >= ReferenceCount)
                {
                    Release();
                }
                
                // 发送Use信号
                SendSignalNotify(SignalType.UnUse);
                return;
            }
            
            // 延迟释放
            if (Delay && !IsCounting)
            {
                --ReferenceCount;
                if (0 >= ReferenceCount)
                {
                    // 重新计时
                    RestartTimeCounter();
                }
                
                // 发送Use信号
                SendSignalNotify(SignalType.UnUse);
            }
        }

#endregion

#region Delay

        /// <summary>
        /// 倒计时计数器
        /// </summary>
        public ITimeCounter CountDown { get; private set; } = null;

        /// <summary>
        /// 倒计时中
        /// </summary>
        public bool IsCounting => CountDown?.IsCounting ?? false;        
        
        /// <summary>
        /// 释放倒计时完毕标志位
        /// <para>* 只有加载为：延迟释放或者倒计时释放时，才有效。</para>
        /// </summary>
        public bool IsCountDown { get; protected set; } = false;

        /// <summary>
        /// 冷却时间
        /// <para>* 单位 : 秒</para>
        /// <para>* 当冷却时间小于等于0秒时，会触发自动释放。</para>
        /// </summary>
        public float CoolTime { get; private set; } = 30.0f;
        
        /// <summary>
        /// 重置释放倒计时
        /// </summary>
        public void RestartTimeCounter()
        {
            IsCountDown = false;
            
            CountDown?.End();
            CountDown?.Restart();
        }

        /// <summary>
        /// 重置释放倒计时
        /// </summary>
        public void ResetTimeCounter()
        {
            IsCountDown = false;
            
            CountDown?.End();
            CountDown?.Reset();
        }

        /// <summary>
        /// 停止加载时间倒计时
        /// </summary>
        public void StopTimeCounter()
        {
            CountDown?.End();
        }

        /// <summary>
        /// 更新时间
        /// </summary>
        /// <param name="iDeltaTime">变化时间</param>
        public void UpdateTime(float iDeltaTime)
        {
            // 非延迟类
            if(!Delay) return;

            // 更新计时器
            CountDown?.Update(iDeltaTime);
        }

        /// <summary>
        /// 计数器计数更新事件
        /// </summary>
        /// <param name="iCurValue">当前值</param>
        /// <param name="iDstValue">目标值</param>
        /// <param name="iType">计数器类型</param>
        /// <param name="iState">计数器状态</param>
        /// <param name="iLoop">循环标志位</param>
        private void OnCounterUpdated(float iCurValue, float iDstValue, CounterType iType, CounterState iState, bool iLoop)
        {
            // 不是计时结束
            if(CounterState.End != iState) return;
            // 没有倒计数完全结束
            if(0.0f < iCurValue) return;
            IsCountDown = true;
                    
            // 释放
            Release();
        }

#endregion

#region Signal
        
        

#endregion

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            Key = null;
            Async = false;
            Options = (int)LoadOptions.Auto;
            From = LoadFrom.Unknown;
            ReferenceCount = 0;

            IsCountDown = false;
            CoolTime = Const.DefaultCoolTime;
            StopTimeCounter();
        }

        /// <summary>
        /// 释放自身
        /// </summary>
        /// <returns>true:释放成功; false:释放失败;</returns>
        protected abstract bool ReleaseSelf();

        /// <summary>
        /// 释放
        /// </summary>
        /// <returns>true:释放成功; false:释放失败;</returns>
        private bool Release()
        {
            // 永生
            if (Immortal) return true;
            
            if (!ReleaseSelf())
            {
                this.Error("Release():ReleaseSelf() Failed!(Key:{0} DstGroup:{1} Options:{2} ReferenceCount:{3} CoolTime:{4}-{5})", 
                    Key, _dstNotifyDstGroup, Options, ReferenceCount, CoolTime, IsCounting ? "Counting" : "Stopped");
                // 取消订阅
                return UnSubscribe();
            }
            
            // 发送释放信号
            if (!SendSignalNotify(SignalType.Unload))
            {
                return false;
            }

            // 取消订阅
            return UnSubscribe();
        }

    }
}